#pragma once
#include <iostream>
#include <memory>
#include <assert.h>
#include <fstream>
#include <string>
#include <sstream>
#include "util.hpp"
#include "message.hpp"

namespace mylog{
    class LogSink{
    public:
        using ptr = std::shared_ptr<LogSink>;
        LogSink(){}
        virtual ~LogSink(){}
        virtual void Log(const char* data, size_t len) = 0;
    };

    class StdSink : public LogSink{
    public:
        using ptr = std::shared_ptr<StdSink>;
        void Log(const char* data, size_t len){
            std::cout.write(data, len);
        }
    };

    class FileSink : public LogSink{
    private:
        std::string _filename;
        std::ofstream _ofs;
    public:
        using ptr = std::shared_ptr<FileSink>;
        FileSink(const std::string& filename): _filename(filename){
            Util::FileUtil::CreateDir(Util::FileUtil::Path(filename));
            _ofs.open(filename, std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
        }
        void Log(const char* data, size_t len){
            _ofs.write(data, len);
            if(_ofs.good() == false){
                std::cout << "日志输出文件失败！\n";
            }
        }
    };

    class RollSink : public LogSink{
    private:
        std::string _basename;
        std::ofstream _ofs;
        size_t _max_size = 0;
        size_t _cur_size = 0;
    private:
        void InitFile(){
            if(_ofs.is_open() == false || _cur_size >= _max_size){
                _ofs.close();
                std::string filename = CreateFilename();
                _ofs.open(filename, std::ios::binary | std::ios::app);
                assert(_ofs.is_open());
                _cur_size = 0;
            }
        }
        std::string CreateFilename(){
            std::stringstream ss;
            time_t t = time(NULL);
            struct tm tl;
            localtime_r(&t, &tl);
            ss << _basename;
            ss << tl.tm_year + 1900;
            ss << tl.tm_mon + 1;
            ss << tl.tm_mday;
            ss << tl.tm_hour;
            ss << tl.tm_min;
            ss << tl.tm_sec;
            ss << ".log";
            return ss.str();
        }
    public:
        RollSink(const std::string& basename, size_t max_size): 
        _basename(basename), _max_size(max_size){
            Util::FileUtil::CreateDir(Util::FileUtil::Path(basename));
        }
        void Log(const char* data, size_t len){
            InitFile();
            _ofs.write(data, len);
            if(_ofs.good() == false){
                std::cout << "日志输出文件失败！\n";
            }
            _cur_size += len;
        }
    };

    class SkinFactory{
    public:
        template<class SinkType, class ...Args>
        static LogSink::ptr Build(Args &&...args){
            return std::make_shared<SinkType>(std::forward<Args>(args)...);
        }
    };
}